package com.ruoyi.web.controller.api.webtool;

import com.ruoyi.api.domain.webtool.FileEntity;
import com.ruoyi.common.annotation.NoLoginRequired;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.config.ServerConfig;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;

import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.quartz.task.ClassifyImageTask;
import com.ruoyi.web.controller.api.APIController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.concurrent.CompletableFuture;

@RestController
@RequestMapping("/api/classifyImage")
@NoLoginRequired
public class classifyImage extends APIController {

    @Autowired
    private ServerConfig serverConfig;

    @Autowired
    private ClassifyImageTask classifyImageTask;

    private static final String FILE_DELIMETER = ",";

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 图片上传（多个）
     */
    @PostMapping("/uploads")
    @ResponseBody
    public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception {
        try {
            if (files == null || files.isEmpty()) {
                return AjaxResult.error("请选择要上传的文件");
            }
            // 上传文件路径
            String uploadPath = RuoYiConfig.getUploadPath();
            String filePath = uploadPath + File.separator + "temp";
            List<String> urls = new ArrayList<String>();
            List<String> fileNames = new ArrayList<String>();
            List<String> newFileNames = new ArrayList<String>();
            List<String> originalFilenames = new ArrayList<String>();
            for (MultipartFile file : files) {
                // 上传并返回新文件名称
                String fileName = FileUploadUtils.upload(filePath, file, MimeTypeUtils.IMAGE_EXTENSION, true);
                String url = serverConfig.getUrl() + fileName;
                urls.add(url);
                fileNames.add(fileName);
                newFileNames.add(FileUtils.getName(fileName));
                originalFilenames.add(file.getOriginalFilename());
            }
            AjaxResult ajax = AjaxResult.success();
            ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
            ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
            ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
            ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
            return ajax;
        } catch (Exception e) {
            logger.error("文件上传失败", e.getMessage());
            return AjaxResult.error(e.getMessage());
        }
    }

    /**
     * 图片获取
     */
    @PostMapping("/list")
    @ResponseBody
    public AjaxResult list(@RequestParam(value = "type", required = false, defaultValue = "0") String type, @RequestParam(value = "path", required = false, defaultValue = "") String relativePath) throws Exception {
        try {
            String uploadPath = RuoYiConfig.getUploadPath();
            List<FileEntity> fileEntities = new ArrayList<>();

            // 根据类型确定基础文件夹
            String folder = "1".equals(type) ? "recipe" : "temp";
            File rootPath = new File(uploadPath, folder);

            // 确保根目录存在
            if (!rootPath.exists() && !rootPath.mkdirs()) {
                logger.error("创建目录失败：" + rootPath.getAbsolutePath());
                return AjaxResult.error("无法创建根目录");
            }

            // 构建目标目录
            File directory = new File(rootPath, relativePath);
            if (!directory.exists() || !directory.isDirectory()) {
                return AjaxResult.error("指定路径无效或不是文件夹");
            }

            // 获取并遍历目录下的文件和子文件夹
            File[] files = directory.listFiles();
            if (files == null || files.length == 0) {
                return AjaxResult.success(fileEntities);
            }

            for (File file : files) {
                FileEntity fileEntity = new FileEntity();
                fileEntity.setFileName(file.getName());
                if (file.isFile()) {
                    BasicFileAttributes attrs = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
                    fileEntity.setUrl(serverConfig.getUrl() + "/profile/upload/" + folder + "/" + relativePath + "/" + file.getName());
                    fileEntity.setPath(file.getPath());
                    fileEntity.setRelativePath(relativePath);
                    fileEntity.setFileSize(String.valueOf(file.length()));
                    fileEntity.setMimeType(Files.probeContentType(file.toPath()));
                    fileEntity.setCreateTime(new Date(attrs.creationTime().toMillis()));
                    fileEntity.setUpdateTime(new Date(attrs.lastModifiedTime().toMillis()));
                    fileEntity.setFileExt(file.getName().substring(file.getName().lastIndexOf(".")));
                    fileEntity.setIsDir(false);
                } else {
                    fileEntity.setIsDir(true);
                    fileEntity.setRelativePath(relativePath.isEmpty() ? file.getName() : relativePath + "/" + file.getName());
                }
                fileEntities.add(fileEntity);
            }

            return success(fileEntities);
        } catch (Exception e) {
            logger.error("文件获取失败", e);
            return AjaxResult.error(e.getMessage());
        }
    }

    @PostMapping("/classify")
    @ResponseBody
    public AjaxResult classify() throws Exception {
        try {
            String uploadPath = RuoYiConfig.getUploadPath();
            File srcPath = new File(uploadPath, "temp");
            File destPath = new File(uploadPath, "recipe");

            // 确保源目录存在
            if (!srcPath.exists()) {
                return AjaxResult.error("请先上传图片");
            }
            // 确保目标目录存在
            if (!destPath.exists() && !destPath.mkdirs()) {
                logger.error("创建目录失败：" + destPath.getAbsolutePath());
                return AjaxResult.error("创建目录失败");
            }
            File[] files = srcPath.listFiles();
            if (files == null || files.length == 0) {
                return AjaxResult.error("请先上传图片");
            }

            // 第一阶段：创建所有需要的目录
            Set<String> directories = new HashSet<>();
            for (File file : files) {
                if (file.isFile()) {
                    String fileName = file.getName();
                    if (fileName.lastIndexOf("-") > 0 && fileName.lastIndexOf(".") > 2) {
                        String folder = fileName.substring(0, fileName.lastIndexOf("-"));
                        directories.add(folder);
                    }
                }
            }
            
            // 并行创建目录
            List<CompletableFuture<Void>> createDirFutures = new ArrayList<>();
            StringBuilder dirMsg = new StringBuilder();
            for (String folder : directories) {
                createDirFutures.add(CompletableFuture.runAsync(() -> {
                    File directory = new File(destPath, folder);
                    synchronized (destPath) {
                        if (!directory.exists()) {
                            try {
                                if (!directory.mkdirs()) {
                                    synchronized (dirMsg) {
                                        dirMsg.append("创建目录失败：").append(directory.getAbsolutePath()).append(" ");
                                    }
                                    logger.error("创建目录失败：" + directory.getAbsolutePath());
                                } else {
                                    logger.info("成功创建目录：" + directory.getAbsolutePath());
                                }
                            } catch (SecurityException e) {
                                synchronized (dirMsg) {
                                    dirMsg.append("无权限创建目录：").append(directory.getAbsolutePath()).append(" ");
                                }
                                logger.error("无权限创建目录：" + directory.getAbsolutePath(), e);
                            }
                        }
                    }
                }, threadPoolTaskExecutor));
            }
            
            // 等待目录创建完成
            CompletableFuture.allOf(createDirFutures.toArray(new CompletableFuture[0])).join();
            
            if (dirMsg.length() > 0) {
                return AjaxResult.error(dirMsg.toString());
            }

            // 第二阶段：移动文件
            List<CompletableFuture<Void>> moveFileFutures = new ArrayList<>();
            StringBuilder fileMsg = new StringBuilder();
            
            for (File file : files) {
                if (file.isFile()) {
                    moveFileFutures.add(CompletableFuture.runAsync(() -> {
                        try {
                            moveFile(file, destPath);
                        } catch (Exception e) {
                            synchronized (fileMsg) {
                                fileMsg.append(e.getMessage()).append(" ");
                            }
                            logger.error("文件移动失败：" + file.getAbsolutePath(), e);
                        }
                    }, threadPoolTaskExecutor));
                }
            }

            // 等待所有文件移动完成
            CompletableFuture.allOf(moveFileFutures.toArray(new CompletableFuture[0])).join();

            if (fileMsg.length() <= 0) {
                fileMsg.append("文件分类成功，共处理").append(moveFileFutures.size()).append("个文件");
            }
            return success(fileMsg.toString());
        } catch (Exception e) {
            logger.error("文件获取失败", e);
            return AjaxResult.error(e.getMessage());
        }
    }

    private void moveFile(File file, File destPath) throws Exception {
        String fileName = file.getName();
        if (fileName.lastIndexOf("-") <= 0 || fileName.lastIndexOf(".") <= 2) {
            throw new Exception("文件名 " + fileName + " 格式不正确，应该以-分割，以数字结尾，如 1-2.jpg");
        }

        String stepSortStr = fileName.substring(fileName.lastIndexOf("-") + 1, fileName.lastIndexOf("."));
        if (!stepSortStr.matches("^\\d+$")) {
            throw new Exception("文件名 " + fileName + " 格式不正确，应该以-分割，以数字结尾，如 1-2.jpg");
        }

        String folder = fileName.substring(0, fileName.lastIndexOf("-"));
        String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1);
        
        File directory = new File(destPath, folder);
        File destFile = new File(directory, fileName);

        for (String str : MimeTypeUtils.IMAGE_EXTENSION) {
            if (str.equalsIgnoreCase(fileExt)) {
                Files.move(file.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            }
        }
    }

    /**
     * 删除文件/文件夹
     */
    @PostMapping("/remove")
    @ResponseBody
    public AjaxResult remove(@RequestParam(value = "type", required = false, defaultValue = "0") String type, @RequestParam(value = "path", required = false, defaultValue = "") String relativePath) throws Exception {
        try {
            String uploadPath = RuoYiConfig.getUploadPath();
            List<FileEntity> fileEntities = new ArrayList<>();

            // 根据类型确定基础文件夹
            String folder = "1".equals(type) ? "recipe" : "temp";
            File rootPath = new File(uploadPath, folder);

            // 确保根目录存在
            if (!rootPath.exists() && !rootPath.mkdirs()) {
                logger.error("创建目录失败：" + rootPath.getAbsolutePath());
                return AjaxResult.error("无法创建根目录");
            }

            // 判断目标目录是否存在
            File directory = new File(rootPath, relativePath);
            if (!directory.exists()) {
                return AjaxResult.error("文件或文件夹不存在：" + directory.getName());
            }

            if (directory.isDirectory()) {
                // 删除目录下的文件和子文件夹
                Stack<File> stack = new Stack<>();
                stack.push(directory); // 将目录压入栈
                while (!stack.isEmpty()) {
                    File current = stack.pop();

                    if (current.isDirectory()) {
                        File[] files = current.listFiles();
                        if (files != null && files.length > 0) {
                            // 如果目录不为空，将其内容压入栈
                            for (File file : files) {
                                stack.push(file);
                            }
                        } else {
                            // 如果目录为空，直接删除
                            if (!current.delete()) {
                                return AjaxResult.error("删除文件夹 " + current.getName() + " 失败"); // 删除失败
                            }
                        }
                    } else {
                        // 如果是文件，直接删除
                        if (!current.delete()) {
                            return AjaxResult.error("删除文件 " + current.getName() + " 失败"); // 删除失败
                        }
                    }
                }
                if (directory.exists()) {
                    if (!directory.delete()) {
                        return AjaxResult.error("删除文件夹 " + directory.getName() + " 失败"); // 删除失败
                    }
                }
            } else {
                // 如果是文件，直接删除
                if (!directory.delete()) {
                    return AjaxResult.error("删除文件 " + directory.getName() + " 失败"); // 删除失败
                }
            }

            return success();
        } catch (Exception e) {
            logger.error("文件删除失败", e);
            return AjaxResult.error(e.getMessage());
        }
    }

    /**
     * 绑定图片
     */
    @PostMapping("/bind")
    public AjaxResult bind() throws Exception {
        try {
            classifyImageTask.runTask();
            return success();
        } catch (Exception e) {
            logger.error("绑定图片失败", e);
            return AjaxResult.error(e.getMessage());
        }
    }
}
